#include "StdAfx.h"
#include "OpenGLrenderer.h"

namespace banknamespace
{
	////////////////////////////////////////
	///// General function
	////////////////////////////////////////

	

	////////////////////////////////////////
	///// end of General function
	////////////////////////////////////////

	OpenGLrenderer::OpenGLrenderer()
	{

	}

	OpenGLrenderer::~OpenGLrenderer(void)
	{
		ShutdownOpenGL();
	}

	void OpenGLrenderer::init( HWND& m_hWnd, HDC& m_hdcWindow )
	{
		this->m_hdcWindow = &m_hdcWindow;

		InitializeOpenGL();
		glEnable( GL_DEPTH_TEST );
		glEnable( GL_CULL_FACE);
		glMatrixMode( GL_MODELVIEW );
	}

	///// GL method
	void OpenGLrenderer::clear()
	{
		glClearColor (0, 0, 0.25f, 1.0f);
		glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	}
	void OpenGLrenderer::setProjection(float fov, float aspect, float znear, float zfar)
	{
		glMatrixMode( GL_PROJECTION );
		glLoadIdentity();
		gluPerspective( fov, aspect, znear, zfar );

		glMatrixMode( GL_MODELVIEW );
	}

	void OpenGLrenderer::loadMatrix(float* matrix)
	{
		glLoadMatrixf( matrix );
	}
	void OpenGLrenderer::swapBuffer()
	{
		SwapBuffers(*m_hdcWindow);
	}


	///// use Devil to load texture
	unsigned int OpenGLrenderer::loadTexture( const char* szFilePath )
	{
		    //int width,height,nChannels;

			ilInit();
			ilEnable( IL_ORIGIN_SET );
			ilOriginFunc( IL_ORIGIN_UPPER_LEFT );

			ILuint idImage = ilGenImage();
			ilBindImage( idImage );
			
			size_t requiredSize = 0;
			::mbstowcs_s(&requiredSize,NULL,0,szFilePath,0);
			wchar_t* w_szTexturePath = new wchar_t[requiredSize + 1];
			::mbstowcs_s(&requiredSize, w_szTexturePath,requiredSize + 1, szFilePath,requiredSize);

			ilLoadImage( w_szTexturePath );

			delete w_szTexturePath;

			if (IL_NO_ERROR != ilGetError())
			{
				return 0;
			}

			ILuint width = ilGetInteger( IL_IMAGE_WIDTH );
			ILuint height = ilGetInteger( IL_IMAGE_HEIGHT );
			ILuint format = ilGetInteger( IL_IMAGE_FORMAT );
			ILuint type = ilGetInteger( IL_IMAGE_TYPE );
			ILuint nChannels = ilGetInteger( IL_IMAGE_CHANNELS );

			GLuint idTexture;
			glGenTextures(1, &idTexture);
			glBindTexture( GL_TEXTURE_2D, idTexture );

			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
			glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, format, type, ilGetData() );
			
			glGenerateMipmap( GL_TEXTURE_2D );

		    ilDeleteImage( idImage );
			
			return idTexture;
	}

	unsigned int OpenGLrenderer::loadMemoryTexture( unsigned char* pixels, int width, int height )
	{
		    //int width,height,nChannels;
		    //unsigned char* pixels = stbi_load(szFilePath, &width, &height, &nChannels, 4);
		    // ... process data if not NULL ... 
		    // ... x = width, y = height, n = # 8-bit components per pixel ...
		    // ... replace '0' with '1'..'4' to force that many components per pixel
		    // ... but 'n' will always be the number that it would have been if you said 0
			//CONST wchar *lp
			if (!pixels)
			{
				//MessageBox(NULL, L"File load error", L"Error", 0);
				return 0;
			}

			GLuint idTexture;
			glGenTextures(1, &idTexture);
			glBindTexture( GL_TEXTURE_2D, idTexture );

			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
			//glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
			glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels );
			
			glGenerateMipmap( GL_TEXTURE_2D );

		    //delete[] pixels;
			
			return idTexture;
	}

	void OpenGLrenderer::bindTexture( unsigned int textureState, unsigned int textureId )
	{
		unsigned int startByte = GL_TEXTURE0;
		startByte += textureState;
		
		glActiveTexture( startByte );
		glEnable( GL_TEXTURE_2D );
		glBindTexture( GL_TEXTURE_2D, textureId );
	}
	void OpenGLrenderer::disableTexture( unsigned int textureState )
	{
		unsigned int startByte = GL_TEXTURE0;
		startByte += textureState;

		glActiveTexture( startByte );
		glDisable( GL_TEXTURE_2D );
	}

	///// Shader
	int OpenGLrenderer::createProgram(const char* programName, std::vector<attrStruct>& attrString, std::vector<const char*>& uniformString)
	{
		return shaderManager.createProgram(programName, attrString, uniformString);
	}
	void OpenGLrenderer::useProgram(int progID)
	{
		shaderManager.useProgram(progID);
	}
	int OpenGLrenderer::getLocation(const char* name)
	{
		return shaderManager.getLocation(name);
	}
	void OpenGLrenderer::setVariable(int ulocation, shaderType type, void* value)
	{
		shaderManager.setVariable(ulocation, type, value);
	}
	
	/// Buffer
	unsigned int OpenGLrenderer::createBuffer( void* start, int bufferSize, bufferType type )
	{
		GLuint result;
		glGenBuffers(1, &result);
		unsigned int btype;
		if ( type == B_INDEXBUFFER )
		{
			btype = GL_ELEMENT_ARRAY_BUFFER;
		}
		else
		{
			btype = GL_ARRAY_BUFFER;
		}
		glBindBuffer( btype, result );	// TODO GL_ELEMENT_ARRAY_BUFFER
		glBufferData( btype, bufferSize, start, GL_STATIC_DRAW );
		
		return result;
	}

	void OpenGLrenderer::drawIndex(unsigned int idVBO, unsigned int idIBO, int strideSize, unsigned int startVertex, unsigned int endVertex, int indexCount, int startIndex, int baseVertex)
	{
		glBindBuffer( GL_ARRAY_BUFFER, idVBO );
		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, idIBO );

		std::vector<attrStruct> attributeArr = shaderManager.attributeProg[shaderManager.currentProgram];

		// set vertex pointer
		for (unsigned int i = 0; i < attributeArr.size(); ++i)
		{
			glEnableVertexAttribArray(attributeArr[i].id);

			switch (attributeArr[i].attrType)
			{
			case S_FLOAT4 : 
				{
					glVertexAttribPointer( attributeArr[i].id, 4, GL_FLOAT, false, strideSize, reinterpret_cast< const GLvoid* >(attributeArr[i].offset) );
					break;
				}
			case S_FLOAT3 : 
				{
					glVertexAttribPointer( attributeArr[i].id, 3, GL_FLOAT, false, strideSize, reinterpret_cast< const GLvoid* >(attributeArr[i].offset) );
					break;
				}
			case S_FLOAT2 : 
				{
					glVertexAttribPointer( attributeArr[i].id, 2, GL_FLOAT, false, strideSize, reinterpret_cast< const GLvoid* >(attributeArr[i].offset) );
					break;
				}
			default: break;
			}
		}

		// draw
		unsigned int buf = startIndex * sizeof(unsigned int);
		glDrawRangeElementsBaseVertex(GL_TRIANGLES, startVertex, endVertex, indexCount, GL_UNSIGNED_INT, (void*)buf, baseVertex);
		//glDrawRangeElements( GL_TRIANGLES, 0, endVertex, indexCount, GL_UNSIGNED_INT, (void*)buf );
		//glDrawElements( GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);

		// disable
		for (unsigned int i = 0; i < attributeArr.size(); ++i)
		{
			glDisableVertexAttribArray(attributeArr[i].id);
		}
	}

	///// test draw
	void OpenGLrenderer::draw()
	{
		glColor4f(1,1,1,1);
		glBegin( GL_TRIANGLES );
			glVertex3f(-1,0,0);
			glVertex3f(1,0,0);
			glVertex3f(0,1,0);
		glEnd();
	}

	///// protected
	void OpenGLrenderer::InitializeOpenGL()
	{
		assert( m_hdcWindow );

		// Set the window pixel format
		//
		PIXELFORMATDESCRIPTOR pixelFormatDescriptor = {0};

		pixelFormatDescriptor.nSize = sizeof( pixelFormatDescriptor );
		pixelFormatDescriptor.nVersion = 1;

		pixelFormatDescriptor.dwFlags = 
			PFD_DRAW_TO_WINDOW | 
			PFD_SUPPORT_OPENGL | 
			PFD_DOUBLEBUFFER;
		pixelFormatDescriptor.dwLayerMask = PFD_MAIN_PLANE;
		pixelFormatDescriptor.iPixelType = PFD_TYPE_RGBA;
		pixelFormatDescriptor.cColorBits = 32;
		pixelFormatDescriptor.cDepthBits = 32;

		int pixelFormat = ChoosePixelFormat( *m_hdcWindow, &pixelFormatDescriptor );
		assert (pixelFormat != 0);
		SetPixelFormat( *m_hdcWindow, pixelFormat, &pixelFormatDescriptor );

		// Create the OpenGL render context
		//
		m_renderingContext = wglCreateContext( *m_hdcWindow );
		wglMakeCurrent ( *m_hdcWindow, m_renderingContext );
		//g_hGLRenderingContext = m_renderingContext;

		// Hook up the main and local rendering contexts so that they share the same VBOs and such
		//g_hGLRenderingContextForPrecaching = wglCreateContext( m_hdcWindow );
			
		//BOOL success = wglShareLists( g_hGLRenderingContext, g_hGLRenderingContextForPrecaching );
		//assert( success );

		glewInit();

		glEnable( GL_DEPTH_TEST );

		//return m_renderingContext;
	}

	void OpenGLrenderer::ShutdownOpenGL()
	{
		//wglDeleteContext( g_hGLRenderingContextForPrecaching );
		//g_hGLRenderingContextForPrecaching = 0;

		wglMakeCurrent( NULL, NULL );
		wglDeleteContext( m_renderingContext );
		m_renderingContext = 0;
	}
}
